#include "pam_b64.c"
#include "pgconn.h"

/*--------------------------------------------------------------------------------
Create connection with BD
----------------------------------------------------------------------------------*/
CONNECTION_DB init_db(modopt_t object){
  
  CONNECTION_DB connection = malloc(sizeof(struct pgconn_t));

  if (object==NULL){
    connection->conf = load_conf(NULL);
  }
  else if (object!=NULL && object->fileconf!=NULL){
    connection->conf = load_conf(object->fileconf);
  }
  else if (object!=NULL){
    connection->conf = object;
  }
  else
    connection->conf = NULL;

  if (connection->conf->debug >= DB_DEBUG){
    SYSLOG("INIT_DB CONNECTION->CONF: %d", (int) connection->conf);
  }


  if (connection->conf !=NULL){
    char string_conn[256];

    sprintf(string_conn, "host=%s port=%d dbname=%s user=%s password=%s", 
	    connection->conf->host,
	    connection->conf->port, connection->conf->db,
	    connection->conf->user, connection->conf->passwd);
    
    connection->pgconn = PQconnectdb(string_conn);
    if (PQstatus(connection->pgconn)!=CONNECTION_OK){
      
      //debug ligado
      if (connection->conf->debug >= DB_DEBUG){
	SYSLOG("INIT_DB POSTGRES MESSAGE: %s", PQerrorMessage(connection->pgconn));
      }

      connection=NULL;
    }

  }
  else
    connection =  NULL;
  return connection;
}

/*---------------------------------------------------------------------
Free memory used by DB connection

-----------------------------------------------------------------------*/
void free_db(CONNECTION_DB connection){
  
  if (connection->pgconn !=NULL){
    
    PQfinish(connection->pgconn);
  }
  if (connection!=NULL)
    free(connection);
  if (connection->conf->debug>=DB_DEBUG){
    SYSLOG("FREE_DB FINISH");
  }

}
/*----------------------------------------------------
Open and read configuration file for DB connection

-----------------------------------------------------*/
modopt_t load_conf(char * filename){

  modopt_t conf = malloc (sizeof (struct modopt_s));

  FILE * f = NULL;
  
  if (filename==NULL)
    f = fopen(DB_FILE_CONF, "r");
  else
    f = fopen(filename, "r");
    
  if (f!=NULL){
    int erro=0;
    char * buffer = malloc(sizeof(char)*256);
    int i=1;
    
    while(i++<=5 && erro==0) 
      erro = get_next_line(f,buffer,conf);
    
    if (erro==1)
      conf = NULL;
  }
  else{
    conf = NULL;
  }
  return conf;
}

int get_op_param(char * param){
  int res = 0;
  if (strstr(param,"host")!=NULL)
    res=1;
  else if (strstr(param,"port")!=NULL)
    res=2;
  else if (strstr(param,"db")!=NULL)
    res=3;
  else if (strstr(param,"user")!=NULL)
    res=4;
  else if (strstr(param,"password")!=NULL)
    res=5;
  return res;
}

/*--------------------------------------------------------------
get the next line from conf file for database
----------------------------------------------------------------*/
int get_next_line(FILE * f, char * buffer, modopt_t conf){
  
  buffer = fgets(buffer, 255, f);
  
  int erro=0;
  char * init = strstr(buffer,"=");
  char * param = (char *)malloc(sizeof(char)*(init - buffer+1));
  strncpy(param,buffer,init-buffer);
  
  if (init!=NULL && strlen(++init)>0){
    char * aux = malloc(sizeof(char)*(strlen(init)+1));
    strcpy(aux,init);
    remove_enter(aux);
    int op = get_op_param(param);
    
    switch(op){
    case 0:
      erro=1;
      break;
    case 1: 
      conf->host = aux;
      break;
    case 2: 
      conf->port = atoi(aux);
      break;
    case 3: 
      conf->db = aux;
      break;
    case 4:
      conf->user = aux;
      break;
    case 5:
      conf->passwd = aux;
      break;
    }
  }
  else
    erro=1;
    
  return erro;
}

void remove_enter(char * buffer){
  while (*buffer!='\n' && *buffer!='\0')
    buffer++;
  *buffer='\0';
}


//-----------------------------------------------------------------------------

/*-----------------------------------------------------------------------------
returns 0 for invalid verification and 1 for valid
------------------------------------------------------------------------------*/
int verify_password(CONNECTION_DB conn, const char * login, const char * password){

  int res=0;
  
  if(conn!=NULL){
    char string_conn[256];
    sprintf(string_conn, "SELECT * FROM PAMPG_VERIFY_PASSWD('%s','%s');",login,password);
   
       
    PGresult * result = NULL;
  
    result = PQexec(conn->pgconn, string_conn);

    if (conn->conf->debug >= DB_DEBUG){
      SYSLOG("POSTGRES MESSAGE %s", PQresultErrorMessage(result));
    }
    
    if (result!=NULL && PQntuples(result)> 0 && strcmp("", PQgetvalue(result,0,0)) !=0){
    
      res =  atoi(PQgetvalue(result,0,0));

    } 

    PQclear(result);
  }
  if (conn->conf->debug >= DB_DEBUG){
    SYSLOG("VERIFY PASSWORD RETURN %d", res);
  }
    
  return res;
  
}

/*-----------------------------------------------------------------------------
modify current password. Returns 0 if it fails, 1 if it not fail. 
------------------------------------------------------------------------------*/
int modify_password(CONNECTION_DB conn, const char * login, const char * newpasswd){
  int res=0;
  
  if(conn!=NULL){
    char string_conn[256];
    sprintf(string_conn, "SELECT * FROM PAMPG_MODIFY_PASSWD('%s', '%s');", login, newpasswd);
  
    PGresult * result = NULL;
  
    result = PQexec(conn->pgconn, string_conn);

    if (conn->conf->debug == DB_DEBUG){
      SYSLOG("POSTGRES MESSAGE %s", PQresultErrorMessage(result));
    }
    
    if (result!=NULL && PQntuples(result)> 0 && strcmp("", PQgetvalue(result,0,0)) !=0){
      res =  atoi(PQgetvalue(result,0,0));
    }
    
    PQclear(result);
  }
  
  if (conn->conf->debug >= DB_DEBUG){
    SYSLOG("MODIFY_PASSWORD RETURN %d", res);
  }
    
  return res;
  
}
/*--------------------------------------------------------------
Verify account expired. Returns 0 if account expired, and 1 if
not expired.
--------------------------------------------------------------*/
int verify_expired(CONNECTION_DB conn, const char * login){
  int res = 0;
  if(conn!=NULL){

    int id = get_user_id(conn, login);

    if (id>=0){
    
      char string_conn[256];
      sprintf(string_conn, "SELECT * FROM PAMPG_VERIFY_EXPIRED('%d');", id);
    
      PGresult * result = NULL;

      result = PQexec(conn->pgconn, string_conn);

      if (conn->conf->debug >= DB_DEBUG){
	SYSLOG("POSTGRES MESSAGE %s", PQresultErrorMessage(result));
      }
    

      if (result!=NULL && PQntuples(result)> 0 && strcmp("", PQgetvalue(result,0,0)) !=0){
	res =  atoi(PQgetvalue(result,0,0));

      }
    
      PQclear(result);
    }
  }

  if (conn->conf->debug == DB_DEBUG){
    SYSLOG("VERIFY_EXPIRED RETURN %d", res);
  }
  
  return res;
  
}

/*--------------------------------------------------------------
Verify wheter user is on wheel group. Returns 0 -> user is 
not on whell group. Returns 1 -> user is on wheel  group.
--------------------------------------------------------------*/
int verify_wheel(CONNECTION_DB conn, const char * login){
  int res =0;

  if(conn!=NULL){

    int id = get_user_id(conn, login);
    if (id>=0){
    
      char string_conn[256];
      sprintf(string_conn, "SELECT * FROM PAMPG_VERIFY_WHEEL('%d');", id);
 
      PGresult * result = NULL;
    
      result = PQexec(conn->pgconn, string_conn);

      if (conn->conf->debug >= DB_DEBUG){
	SYSLOG("POSTGRES MESSAGE %s", PQresultErrorMessage(result));
      }
    
      if (result!=NULL && PQntuples(result)> 0 && strcmp("", PQgetvalue(result,0,0)) !=0){
	res =  atoi(PQgetvalue(result,0,0));
      }
 
      PQclear(result);
    }
  }

  if (conn->conf->debug == DB_DEBUG){
    SYSLOG("VERIFY WHEEL RETURN %d", res);
  }
  
  return res;
  
}

/*--------------------------------------------------------------
Verify whether user is on an group.
Returns 0 -> user is not on group. 1 -> user is on group 
--------------------------------------------------------------*/
int verify_user_on_group(CONNECTION_DB conn, const char * login, const char * group){
  int res =0;

  if (conn!=NULL){

    int u_id = get_user_id(conn, login);
    int g_id = get_group_id(conn,group);
    if (u_id>=0 && g_id>=0){
      char string_conn[255];
      sprintf(string_conn, "SELECT * FROM PAMPG_VERIFY_USER_ON_GROUP('%d', '%d');", u_id,g_id); 
    
      PGresult * result = NULL;
    
      result = PQexec(conn->pgconn, string_conn);
      
      if (conn->conf->debug >= DB_DEBUG){
	SYSLOG("POSTGRES MESSAGE %s", PQresultErrorMessage(result));
      }
      

      if (result!=NULL && PQntuples(result)> 0 && strcmp("", PQgetvalue(result,0,0)) !=0){
	res =  atoi(PQgetvalue(result,0,0));
      }
      
      PQclear(result);
    }
  }
  
  if (conn->conf->debug >= DB_DEBUG){
    SYSLOG("VERIFY USER ON GROUP RETURN %d", res);
  }
  

  return res;
  
}


/*--------------------------------------------------------------
Get ID group. Returns -1 -> group does not exists. 
Returns >=0  ->  ID group.
---------------------------------------------------------------*/

int get_group_id(CONNECTION_DB conn, const char * group){
  int res = -1;
  if (conn!=NULL){
    char string_conn[256];
  
    sprintf(string_conn, "SELECT * FROM PAMPG_GET_GROUP_ID('%s');", group);
  
    PGresult * result = NULL;
    
    result = PQexec(conn->pgconn, string_conn);
  
    if (conn->conf->debug >= DB_DEBUG){
      SYSLOG("POSTGRES MESSAGE %s", PQresultErrorMessage(result));
    }
    
    if (result!=NULL && PQntuples(result)> 0 && strcmp("", PQgetvalue(result,0,0)) !=0){
      res = atoi(PQgetvalue(result,0,0));
    }
  
    PQclear(result);
  }
  
  if (conn->conf->debug >= DB_DEBUG){
    SYSLOG("GET GROUP ID RETURN %d", res);
  }
  

  return res;
}
/*---------------------------------------------------------------
Get user ID. Returns -1 -> user does not exists.
Returns >=0 -> user ID 
-----------------------------------------------------------------*/
int get_user_id(CONNECTION_DB conn,  const char * login){
  int res=-1;
  if (conn!=NULL){
    char string_conn[256];
    PGresult * result = NULL;

    sprintf(string_conn, "SELECT * FROM PAMPG_GET_USER_ID('%s');", login);
    
    result = PQexec(conn->pgconn, string_conn);

    if (conn->conf->debug >= DB_DEBUG){
      SYSLOG("POSTGRES MESSAGE %s", PQresultErrorMessage(result));
    }
    
  
    if (result!=NULL && PQntuples(result)> 0 && strcmp("", PQgetvalue(result,0,0)) !=0){

      res = atoi(PQgetvalue(result,0,0));

    }

    PQclear(result);
    
  }

  if (conn->conf->debug >= DB_DEBUG){
    SYSLOG("GET USER ID RETURN %d", res);
  }
  
  return res;
}

/*---------------------------------------------------------------
Get user ID. Returns 0 -> update unsuccesfull.
Returns 1 -> last_login updated 
-----------------------------------------------------------------*/
int update_user_last_login(CONNECTION_DB conn,  const char * login, int last_login){
  int res=0;
  if (conn!=NULL){
    char string_conn[256];
    PGresult * result = NULL;
    
    int user_id = get_user_id(conn,login);
    if (user_id>=0){

      sprintf(string_conn, "SELECT * FROM PAMPG_UPDATE_USER_LAST_LOGIN(%d, %d);", 
	      user_id, last_login);
    
      result = PQexec(conn->pgconn, string_conn);

      if (conn->conf->debug >= DB_DEBUG){
	SYSLOG("POSTGRES MESSAGE %s", PQresultErrorMessage(result));
      }
    
  
      if (result!=NULL && PQntuples(result)> 0 && strcmp("", PQgetvalue(result,0,0)) !=0){

	res = atoi(PQgetvalue(result,0,0));
	if (res==last_login)
	  res=1;
	else
	  res=0;
      }

      PQclear(result);
    }
  }

  if (conn->conf->debug >= DB_DEBUG){
    SYSLOG("GET USER ID RETURN %d", res);
  }
  
  return res;
}


/*---------------------------------------------------------------
Get user login. Returns NULL -> user does not exists.
Returns != NULL -> user's login
-----------------------------------------------------------------*/
char * get_user_login(CONNECTION_DB conn,  const char * id){
  char * res = NULL;
  if (conn!=NULL){
    char string_conn[256];
    sprintf(string_conn, "SELECT * FROM PAMPG_GET_USER_LOGIN('%s');",id);
  
    PGresult * result = NULL;
    
    result = PQexec(conn->pgconn, string_conn);
  
    if (conn->conf->debug >= DB_DEBUG){
      SYSLOG("POSTGRES MESSAGE %s", PQresultErrorMessage(result));
    }
    
    if (result!=NULL && PQntuples(result)> 0 && strcmp("", PQgetvalue(result,0,0)) !=0){
      res = (char *)strdup(PQgetvalue(result,0,0));
    }
    
  
    PQclear(result);
  }

  if (conn->conf->debug >= DB_DEBUG){
    SYSLOG("GET USER LOGIN RETURN %s", res);
  }
    
  return res;
}

/*---------------------------------------------------------------
Get group name. Returns NULL -> group does not exists.
Returns != NULL -> group's name
-----------------------------------------------------------------*/
char * get_group_name(CONNECTION_DB conn,  const char * id){
  char * res=NULL;
  
  if (conn!=NULL){
    char string_conn[256];
    sprintf(string_conn, "SELECT * FROM PAMPG_GET_GROUP_NAME('%s');", id);
  
    PGresult * result = NULL;
    
    result = PQexec(conn->pgconn, string_conn);
  
    if (conn->conf->debug >= DB_DEBUG){
      SYSLOG("POSTGRES MESSAGE %s", PQresultErrorMessage(result));
    }
    

    if (result!=NULL && PQntuples(result)> 0 && strcmp("", PQgetvalue(result,0,0)) !=0){
      res = (char *)strdup(PQgetvalue(result,0,0));
    }
      
    PQclear(result);
  }

  if (conn->conf->debug >= DB_DEBUG){
    SYSLOG("GET GROUP NAME RETURN %s", res);
  }
  
  return res;
}

/* ***************************
 * Get de two first caracters from passord stored on DB
 * Or caracters from 4 to 12, in case password has been generated by crypt	
 * ***************************/
char * get_crypt_salt(CONNECTION_DB conn, char *login, int type) {

   PGresult *result=NULL;
   char * salt = NULL;
   
   if (conn!=NULL){
     int user_id = get_user_id(conn,login);
     char string_conn[256];
     
     if(type == PW_CRYPT)
       sprintf(string_conn, "SELECT * FROM PAMPG_GET_SALT(%d);",user_id);
     else if(type == PW_CRYPT_MD5)
       sprintf(string_conn, "SELECT * FROM PAMPG_GET_SALT_MD5(%d);",user_id);
     
     result = PQexec(conn->pgconn, string_conn);
    
     if (conn->conf->debug >= DB_DEBUG){
       SYSLOG("POSTGRES MESSAGE %s", PQresultErrorMessage(result));
     }
    
     if (result!=NULL && PQntuples(result)> 0 && strcmp("", PQgetvalue(result,0,0)) !=0){

       salt=strdup(PQgetvalue(result,0,0));
       
     }
     
     PQclear(result);
   }
   if (conn->conf->debug >= DB_DEBUG){
     SYSLOG("GET CRYPT SALT RETURN %s", salt);
   }
   
   return salt;

}

/*-----------------------------------------------------------------------------
retorna 0 se a insercao nao funcionou, 1 se funcionou
------------------------------------------------------------------------------*/
/*
int add_user_on_group(CONNECTION_DB conn, const char * group, const char * user){
  char string_conn[256];
  sprintf(string_conn, "SELECT * FROM GET_USER_ID('%s');", user);
 


  PGresult * result = NULL;
  result = PQexec(conn->pgconn, string_conn);
  char * user_id=NULL;
  if (result!=NULL && PQntuples(result)> 0 && strcmp("", PQgetvalue(result,0,0)) !=0){
    user_id = PQgetvalue(result,0,0);
  }
  //free(string_conn);
  sprintf(string_conn, "SELECT * FROM GET_GROUP_ID('%s');", group);

  //string_conn = concatplus(3, "SELECT * FROM GET_GROUP_ID('",
  //				  group,"');");
  PQclear(result);
  result = PQexec(conn->pgconn, string_conn);
  char * group_id=NULL;
  if (result!=NULL && PQntuples(result)> 0 && strcmp("", PQgetvalue(result,0,0)) !=0){
    group_id = PQgetvalue(result,0,0);
  }
  free(string_conn);
  PQclear(result);
  int res=0;
  if (group_id!=NULL & user_id!=NULL){
    sprintf(string_conn, "SELECT * FROM ADD_USER_GROUP('%d', '%d');",group_id,user_id);  
    //string_conn = concatplus(5,"SELECT * FROM ADD_USER_GROUP('",
    //			  group_id, "','", user_id, "');");
    result = PQexec(conn->pgconn, string_conn);
    if (result!=NULL && PQntuples(result)> 0 && strcmp("", PQgetvalue(result,0,0)) !=0){
      res = atoi(PQgetvalue(result,0,0));
    }
    free(string_conn);
    PQclear(result);
  }
  return res;
}
*/
//------------------------------------------------------------------------

/* *************************
 * Crypt password using crypt with salt or using md5 digest
 *
 * ************************* */
char * encrypt_password(int options, char *pass, char *salt) {

   char *s = NULL;

   switch(options) {

      case PW_CRYPT:
      case PW_CRYPT_MD5:

         s = strdup(crypt(pass, salt));
  
      break;

      case PW_MD5: {

         char *buf;
         int buf_size;
         MHASH handle;
         unsigned char *hash;

         handle = mhash_init(MHASH_MD5);

         if(handle == MHASH_FAILED) {
            SYSLOG("Falha ao iniciar biblioteca mhash");
         } else {
            unsigned int i;
            mhash(handle, pass, strlen(pass));
            hash = mhash_end(handle);

            if(hash != NULL) {
               buf_size = (mhash_get_block_size(MHASH_MD5) * 2)+1;
               buf = (char *)malloc(buf_size);
               bzero(buf, buf_size);

               for(i = 0; i < mhash_get_block_size(MHASH_MD5); i++) 
                  sprintf(&buf[i * 2], "%.2x", hash[i]);

               free(hash);

               s = buf;
            } else {
               s = strdup("!");
            }
 
         }
      }
      break;

      case PW_SHA1: {

         char *buf;
         int buf_size;
         MHASH handle;
         unsigned char *hash;

         handle = mhash_init(MHASH_SHA1);

         if(handle == MHASH_FAILED) {
            SYSLOG("Falha ao iniciar biblioteca mhash");
         } else {
            unsigned int i;
            mhash(handle, pass, strlen(pass));
            hash = mhash_end(handle);

            if(hash != NULL) {
               buf_size = (mhash_get_block_size(MHASH_SHA1) * 2)+1;
               buf = (char *)malloc(buf_size);
               bzero(buf, buf_size);

               for(i = 0; i < mhash_get_block_size(MHASH_SHA1); i++) 
                  sprintf(&buf[i * 2], "%.2x", hash[i]);

               free(hash);

               s = buf;
            } else {
               s = strdup("!");
            }
 
         }
      }
      break;

      case PW_MD5_B64: {

         char *buf;
         int buf_size;
         MHASH handle;
         unsigned char *hash;

         handle = mhash_init(MHASH_MD5);

         if(handle == MHASH_FAILED) {
            SYSLOG("Falha ao iniciar biblioteca mhash");
         } else {

            mhash(handle, pass, strlen(pass));
            hash = mhash_end(handle);

            if(hash != NULL) {
               buf_size = (mhash_get_block_size(MHASH_MD5) * 2)+1;
               buf = (char *)malloc(buf_size);
               bzero(buf, buf_size);

               s = base64(hash, strlen( (char*) hash) );

               free(hash);

            } else {
               s = strdup("!");
            }
 
         }
      }
      break;

      case PW_CLEAR:
      default:
         s = strdup(pass);
   }

   return s;

}
